# _*_ coding: utf-8 _*_
from odoo import models, api
from odoo.tools.misc import OrderedSet


@api.model
def _read_group_raw(self, domain, fields, groupby, offset=0, limit=None, orderby=False, lazy=True):
    self.check_access_rights('read')
    query = self._where_calc(domain)
    fields = fields or [f.name for f in self._fields.itervalues() if f.store]

    groupby = [groupby] if isinstance(groupby, basestring) else list(OrderedSet(groupby))
    groupby_list = groupby[:1] if lazy else groupby
    annotated_groupbys = [self._read_group_process_groupby(gb, query) for gb in groupby_list]
    groupby_fields = [g['field'] for g in annotated_groupbys]
    order = orderby or ','.join([g for g in groupby_list])
    groupby_dict = {gb['groupby']: gb for gb in annotated_groupbys}

    self._apply_ir_rules(query, 'read')
    for gb in groupby_fields:
        assert gb in fields, "Fields in 'groupby' must appear in the list of fields to read (perhaps it's missing in the list view?)"
        assert gb in self._fields, "Unknown field %r in 'groupby'" % gb
        gb_field = self._fields[gb].base_field
        assert gb_field.store and gb_field.column_type, "Fields in 'groupby' must be regular database-persisted fields (no function or related fields), or function fields with store=True"

    aggregated_fields = [
        f for f in fields
        if f != 'sequence'
        if f not in groupby_fields
        for field in [self._fields.get(f)]
        if field
        if field.group_operator
        if field.base_field.store and field.base_field.column_type
    ]

    field_formatter = lambda f: (
        self._fields[f].group_operator,
        self._inherits_join_calc(self._table, f, query),
        f,
    )
    select_terms = ['%s(%s) AS "%s" ' % field_formatter(f) for f in aggregated_fields]

    for gb in annotated_groupbys:
        select_terms.append('%s as "%s" ' % (gb['qualified_field'], gb['groupby']))

    groupby_terms, orderby_terms = self._read_group_prepare(order, aggregated_fields, annotated_groupbys, query)
    from_clause, where_clause, where_clause_params = query.get_sql()
    if lazy and (len(groupby_fields) >= 2 or not self._context.get('group_by_no_leaf')):
        count_field = groupby_fields[0] if len(groupby_fields) >= 1 else '_'
    else:
        count_field = '_'
    count_field += '_count'

    prefix_terms = lambda prefix, terms: (prefix + " " + ",".join(terms)) if terms else ''
    prefix_term = lambda prefix, term: ('%s %s' % (prefix, term)) if term else ''

    query = """
        SELECT min("%(table)s".id) AS id, count("%(table)s".id) AS "%(count_field)s" %(extra_fields)s
        FROM %(from)s
        %(where)s
        %(groupby)s
        %(orderby)s
        %(limit)s
        %(offset)s
    """ % {
        'table': self._table,
        'count_field': count_field,
        'extra_fields': prefix_terms(',', select_terms),
        'from': from_clause,
        'where': prefix_term('WHERE', where_clause),
        'groupby': prefix_terms('GROUP BY', groupby_terms),
        'orderby': prefix_terms('ORDER BY', orderby_terms),
        'limit': prefix_term('LIMIT', int(limit) if limit else None),
        'offset': prefix_term('OFFSET', int(offset) if limit else None),
    }
    self._cr.execute(query, where_clause_params)
    fetched_data = self._cr.dictfetchall()

    if not groupby_fields:
        return fetched_data

    many2onefields = [gb['field'] for gb in annotated_groupbys if gb['type'] == 'many2one']
    if many2onefields:
        data_ids = [r['id'] for r in fetched_data]
        many2onefields = list(set(many2onefields))
        if self._name == "stock.quant" and many2onefields == ["product_id"] and data_ids:
            sql = """
            SELECT 
              stock_quant.id       AS quant_id, 
              product.id           AS product_id, 
              template.name        AS product_name, 
              product.default_code AS default_code,
              attr.name as attr_name
            FROM stock_quant 
              LEFT JOIN product_product product ON stock_quant.product_id = product.id 
              LEFT JOIN product_template template ON product.product_tmpl_id = template.id 
              LEFT JOIN product_attribute_value_product_product_rel rel on product.id = rel.product_product_id
              LEFT JOIN product_attribute_value attr on rel.product_attribute_value_id = attr.id
            WHERE stock_quant.id IN %s
            """
            self.env.cr.execute(sql, (tuple(data_ids),))
            raw_res = self.env.cr.dictfetchall()
            res = {}
            for r in raw_res:
                if r["quant_id"] in res:
                    if res[r["quant_id"]]["attr_name"]:
                        res[r["quant_id"]]["attr_name"] += "," + r["attr_name"]
                    else:
                        res[r["quant_id"]]["attr_name"] = r["attr_name"]
                else:
                    res[r["quant_id"]] = r
            data_dict = {d["quant_id"]: {
                "id": d["quant_id"],
                "product_id": (d["product_id"], "[%s]%s%s" % (d["default_code"], d["product_name"],
                                                              (("(%s)" % d["attr_name"]) if d["attr_name"] else ""))),
            } for d in res.values()}
        else:
            data_dict = {d['id']: d for d in self.browse(data_ids).read(many2onefields)}
        for d in fetched_data:
            d.update(data_dict[d['id']])

    data = map(lambda r: {k: self._read_group_prepare_data(k,v, groupby_dict) for k,v in r.iteritems()}, fetched_data)
    result = [self._read_group_format_result(d, annotated_groupbys, groupby, domain) for d in data]
    if lazy:
        # Right now, read_group only fill results in lazy mode (by default).
        # If you need to have the empty groups in 'eager' mode, then the
        # method _read_group_fill_results need to be completely reimplemented
        # in a sane way
        result = self._read_group_fill_results(
            domain, groupby_fields[0], groupby[len(annotated_groupbys):],
            aggregated_fields, count_field, result, read_group_order=order,
        )
    return result


models.BaseModel._read_group_raw = _read_group_raw
